// ========================================================================
// Copyright (c) 2009 Intalio, Inc.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at 
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses. 
// Contributors:
//    Hugues Malphettes - initial API and implementation
// ========================================================================
package org.eclipse.jetty.osgi.boot.internal.serverfactory;

import java.net.URL;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.server.Server;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;

/**
 * Manages the deployment of jetty server instances.
 * Not sure this is bringing much compared to the JettyServerServiceTracker.
 * 
 * @author hmalphettes
 */
public class JettyServersManagedFactory implements ManagedServiceFactory, IManagedJettyServerRegistry
{

    /**
     * key to configure the server according to a jetty home folder. the value
     * is the corresponding java.io.File
     */
    public static final String JETTY_HOME = "jettyhome";
    /** key to configure the server according to a jetty.xml file */
    public static final String JETTY_CONFIG_XML = "jettyxml";

    /**
     * invoke jetty-factory class. the value of this property is the instance of
     * that class to call back.
     */
    public static final String JETTY_FACTORY = "jettyfactory";

    /**
     * default property in jetty.xml that is used as the value of the http port.
     */
    public static final String JETTY_HTTP_PORT = "jetty.http.port";
    /**
     * default property in jetty.xml that is used as the value of the https
     * port.
     */
    public static final String JETTY_HTTPS_PORT = "jetty.http.port";

    /**
     * Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin service.
     */
    private Map<String, ServerInstanceWrapper> _serversIndexedByPID = new HashMap<String, ServerInstanceWrapper>();
    /**
     * PID -> {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME}
     */
    private Map<String, String> _serversNameIndexedByPID = new HashMap<String, String>();
    /**
     * {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME} -> PID
     */
    private Map<String, String> _serversPIDIndexedByName = new HashMap<String, String>();

    /**
     * Return a descriptive name of this factory.
     * 
     * @return the name for the factory, which might be localized
     */
    public String getName()
    {
        return getClass().getName();
    }

    public void updated(String pid, Dictionary properties) throws ConfigurationException
    {
    	ServerInstanceWrapper serverInstanceWrapper = getServerByPID(pid);
        deleted(pid);
        // do we need to collect the currently deployed http services and
        // webapps
        // to be able to re-deploy them later?
        // probably not. simply restart and see the various service trackers
        // do everything that is needed.
        String name = (String)properties.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
        if (name == null)
        {
        	throw new ConfigurationException(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME,
        			"The name of the server is mandatory");
        }
        serverInstanceWrapper = new ServerInstanceWrapper(name);
        _serversIndexedByPID.put(pid, serverInstanceWrapper);
        _serversNameIndexedByPID.put(pid, name);
        _serversPIDIndexedByName.put(name, pid);
        serverInstanceWrapper.start(new Server(), properties);
    }

    public synchronized void deleted(String pid)
    {
    	ServerInstanceWrapper server = (ServerInstanceWrapper)_serversIndexedByPID.remove(pid);
        String name = _serversNameIndexedByPID.remove(pid);
        if (name != null)
        {
        	_serversPIDIndexedByName.remove(name);
        }
        else
        {
        	//something incorrect going on.
        }
        if (server != null)
        {
            try
            {
            	server.stop();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public synchronized ServerInstanceWrapper getServerByPID(String pid)
    {
    	return _serversIndexedByPID.get(pid);
    }
    
    /**
     * @param managedServerName The server name
     * @return the corresponding jetty server wrapped with its deployment properties.
     */
	public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
    {
    	String pid = _serversPIDIndexedByName.get(managedServerName);
    	return pid != null ? _serversIndexedByPID.get(pid) : null;
    }
        
    /**
     * Helper method to create and configure a new Jetty Server via the ManagedServiceFactory
     * @param contributor
     * @param serverName
     * @param urlsToJettyXml
     * @throws Exception
     */
    public static void createNewServer(Bundle contributor, String serverName, String urlsToJettyXml) throws Exception
    {
        ServiceReference configurationAdminReference =
        	contributor.getBundleContext().getServiceReference( ConfigurationAdmin.class.getName() );

        ConfigurationAdmin confAdmin = (ConfigurationAdmin) contributor.getBundleContext()
        				.getService( configurationAdminReference );   

        Configuration configuration = confAdmin.createFactoryConfiguration(
        		OSGiServerConstants.MANAGED_JETTY_SERVER_FACTORY_PID, contributor.getLocation() );
        Dictionary properties = new Hashtable();
        properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
        
        StringBuilder actualBundleUrls = new StringBuilder();
        StringTokenizer tokenizer = new StringTokenizer(urlsToJettyXml, ",", false);
        while (tokenizer.hasMoreTokens())
        {
        	if (actualBundleUrls.length() != 0)
        	{
        		actualBundleUrls.append(",");
        	}
        	String token = tokenizer.nextToken();
        	if (token.indexOf(':') != -1)
        	{
        		//a complete url. no change needed:
        		actualBundleUrls.append(token);
        	}
        	else if (token.startsWith("/"))
        	{
        		//url relative to the contributor bundle:
        		URL url = contributor.getEntry(token);
        		if (url == null)
        		{
        			actualBundleUrls.append(token);
        		}
        		else
        		{
        			actualBundleUrls.append(url.toString());
        		}
        	}
        		
        }
        
        properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, actualBundleUrls.toString());
        configuration.update(properties);

    }
    
}
